package com.wandian.net
{
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.Endian;
	import com.wandian.utils.Debuger;
	import com.wandian.ui.controls.Alert;

	/**
	 *
	 * @author long
	 */
	public class ServerProxy
	{
		private var parseLength:int=0;
		private var parseState:int=1;
		/**
		 *
		 * @default 客户端请求命令id
		 */
		private var requestKey:int=0;
		/**
		 *
		 * @default 响应的事件命令字典
		 */
		private var responseListeners:Dictionary;
		private var socket:Socket;
		private var byteBuffer:ByteArray;
		/**
		 *
		 * @default 服务端自主推送的消息处理器
		 */
		private var pushDispatcher:PushDispatcher=new PushDispatcher();
		private static const PushCommandPrefix:String="PushCommand_"
		/**
		 *
		 * @default 表明是客户端的请求
		 */
		public static const Request:int=0x03
		/**
		 *
		 * @default
		 */
		public static const Response:int=0x05;
		/**
		 *
		 * @default
		 */
		public static const Push:int=0x06;

		/**
		 *
		 * @param host
		 * @param port
		 */
		public function ServerProxy(host:String, port:int)
		{
			socket=new Socket();
			socket.addEventListener(Event.CONNECT, onConnected);
			socket.addEventListener(Event.CLOSE, onClosed);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, onDataReceived);
			socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
			socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			responseListeners=new Dictionary(true);
			byteBuffer=new ByteArray();
			socket.timeout=3000;
			socket.connect(host, port);
		}

		/**
		 *
		 * @param command
		 * @param listener
		 */
		public function addServerPushListener(command:int, listener:Function):void
		{
			pushDispatcher.addEventListener(PushCommandPrefix + command, listener);
		}

		/**
		 *
		 * @param command 命令id
		 * @param handler 处理服务端响应的中介者里的回调
		 * @param args
		 */
		public function callFormConsole(command:int, handler:Function, args:Array):void
		{
			//创建内容
			var ba:ByteArray=new ByteArray();
			//ba.endian=Endian.LITTLE_ENDIAN;
			requestKey++;
			if (handler != null)
			{
				responseListeners[requestKey]=handler;
			}
			ba.writeByte(Request);
			ba.writeShort(requestKey);
			ba.writeShort(command);
			var l:int=args == null ? 0 : args.length;
			ba.writeByte(l);
			if (l > 0)
			{
				for (var i:int=0; i < args.length; i++)
				{
					var str:String=args[i]
					if (str == null)
					{
						ba.writeShort(0)
					}
					else
					{
						ba.writeUTF(args[i]);
					}
				}
			}
			//正式写入
			socket.writeByte(0x02); //写头标识
			l=ba.length;
			socket.writeInt(l); //写长度信息
			socket.writeBytes(ba, 0, l);
			socket.flush();
		}

		//发送socket
		/**
		 *
		 * @param command 命令id
		 * @param handler 服务端响应之后，回调中介者里的处理方法
		 * @param args
		 */
		public function call(command:int, handler:Function, ... args):void
		{
			//创建内容
			var ba:ByteArray=new ByteArray();
			requestKey++;
			if (handler != null)
			{
				var info:HandlerInfo=new HandlerInfo()
				info.command=command
				info.handler=handler
				responseListeners[requestKey]=info;
			}
			ba.writeByte(Request);
			ba.writeShort(requestKey);
			ba.writeShort(command); //写请求的事件名
			//把参数写入字节流
			var l:int=args == null ? 0 : args.length;
			ba.writeByte(l);
			if (l > 0)
			{
				for (var i:int=0; i < args.length; i++)
				{
					var str:String=args[i]
					if (str == null)
					{
						ba.writeShort(0)
					}
					else
					{
						ba.writeUTF(args[i]);
					}
				}
			}
			//正式写入
			socket.writeByte(0x02); //写头标识
			l=ba.length;
			socket.writeInt(l); //写长度信息
			socket.writeBytes(ba, 0, l);
			socket.flush();
		}

		/**
		 *
		 * @param e
		 */
		private function onClosed(e:Event):void
		{
			Alert.show("socket onClosed");
		}

		private function onConnected(e:Event):void
		{
			Alert.show("socket onConnected");
		}

		//1:处理头标识以及包长度信息,3:处理内容,4:错误纠正重新寻找头标识
		private function onDataReceived(e:ProgressEvent=null):void
		{
			switch (parseState)
			{
				case 1:
					if (socket.bytesAvailable < 5)
					{
						return;
					}
					var b:int=socket.readByte();
					//理论上不会出现此情情况，预防万一
					if (b != 0x02)
					{
						trace("读取错误,准备修正");
						return;
					}
					parseLength=socket.readInt();
					parseState=2;
				case 2:
					trace("socket.bytesAvailable:" + socket.bytesAvailable, "parseLength:" + parseLength)
					if (socket.bytesAvailable < parseLength)
					{
						return;
					}
					byteBuffer.clear();

					socket.readBytes(byteBuffer, 0, parseLength);
					byteBuffer.position=0;
					parse(byteBuffer);

					parseState=1;
					onDataReceived();
			}

		}

		/**
		 * socket错误
		 * @param e
		 */
		private function onError(e:ErrorEvent):void
		{
			Alert.show("socket IO 错误:"+e.toString());
		}

		private function parse(bytes:ByteArray):void
		{
			//
			var type:int=byteBuffer.readByte();
			var o:Object;
			if (type == Response)
			{
				var key:int=bytes.readShort();
				var statusCode:int=bytes.readShort();
				var info:HandlerInfo=responseListeners[key];
				if (info == null)
				{
					return; //理论上不会，错误
				}
				else
				{
					responseListeners[key]=null;

					o=bytes.readObject()
					Debuger.print("Response " + info.command + " (" + statusCode + ") " + o)
					info.handler(statusCode, o);
				}
			}
			else if (type == Push)
			{
				var command:int=bytes.readShort();
				o=bytes.readObject()
				Debuger.print("Push " + command + " " + o)
				pushDispatcher.dispatchEvent(new ServerPushEvent(PushCommandPrefix + command, o))
			}
			else
			{
				trace("未知类型无法解析");
			}
		}
	}

}

//给中介者使用的，在观察者传递过去，命令及回调处理
class HandlerInfo
{
	/**
	 *
	 * @default
	 */
	public var handler:Function
	/**
	 *
	 * @default
	 */
	public var command:int
}
import flash.events.EventDispatcher;

class PushDispatcher extends EventDispatcher
{

}
